#include "lexer.h"

void Token::Dump()
{
    llvm::outs() << "{" << content << ", row = " << row << ", col = " << col << "}\n";
}

bool IsWhiteSpace(char ch)
{
    return ch == ' ' || ch == '\r' || ch == '\n';
}

bool IsDigit(char ch)
{
    return ch >= '0' && ch <= '9';
}

bool IsLetter(char ch)
{
    // a-z, A-Z, _
    return (ch >= 'a' &&  ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_');
}

Lexer::Lexer(llvm::StringRef sourceCode)
{
    BufPtr = sourceCode.begin();
    LineHeadPtr = sourceCode.begin();
    BufEnd = sourceCode.end();
    row = 1;
}

void Lexer::NextToken(Token &token)
{
    token.row = row;

    // 过滤空格
    while (IsWhiteSpace(*BufPtr))
    {
        if (*BufPtr == '\n')
        {
            row += 1;
            LineHeadPtr = BufPtr + 1;
        }
        BufPtr++;
    }

    token.col = BufPtr - LineHeadPtr + 1;

    // 判断是否到结尾了
    if (BufPtr >= BufEnd)
    {
        token.tokenType = TokenType::eof;
        return;
    }

    const char* start = BufPtr;
    // 判断是否为数字
    if (IsDigit(*BufPtr))
    {
        int len = 0;
        int val = 0;
        while (IsDigit(*BufPtr))
        {
            val = val * 10 + *BufPtr++ - '0';
            len++;
        }
        token.value = val;
        token.tokenType = TokenType::number;
        token.content = llvm::StringRef(start, len);
        token.type = CType::getIntTy();
    } 
    else if(IsLetter(*BufPtr)) // 为变量
    {
        while(IsLetter(*BufPtr) || IsDigit(*BufPtr))
        {
            BufPtr++;
        }
        token.tokenType = TokenType::indentifier;
        token.content = llvm::StringRef(start, BufPtr - start);
        if(token.content == "int")
        {
            token.tokenType = TokenType::kw_int;
        }
    }
    else // 为特殊字符
    {
        switch (*BufPtr)
        {
        case '+':
        {
            token.tokenType = TokenType::plus;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case '-':
        {
            token.tokenType = TokenType::minus;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case '*':
        {
            token.tokenType = TokenType::star;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case '/':
        {
            token.tokenType = TokenType::slash;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case '=':
        {
            token.tokenType = TokenType::equal;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case '(':
        {
            token.tokenType = TokenType::l_parent;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case ')':
        {
            token.tokenType = TokenType::r_parent;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case ';':
        {
            token.tokenType = TokenType::semi;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        case ',':
        {
            token.tokenType = TokenType::comma;
            token.content = llvm::StringRef(start, 1);
            BufPtr++;
            break;
        }
        default:
        {
            token.tokenType = TokenType::unknown;
            BufPtr++;
            break;
        }
        }
    }
}
